using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;
using MediaPortal.GUI.Library;
using MediaPortal.InputDevices;
using SevenZip;
using System.Windows.Forms;

namespace myEmulators
{
    public delegate void ExecutorStatusChangedEventHandler(bool isRunning);

    class Executor
    {
        //Handles the parsing of the commands and executes the game

        private const String gameWildcard = "%ROM%";
        private const String gameWithoutExtWildcard = "%ROM_WITHOUT_EXT%";

        static KeyboardHook hook = null;
        static Keys key = Keys.None;
        static Process proc = null;

        public static event ExecutorStatusChangedEventHandler StatusChanged; 

        public static void launchDocument(DBItem item)
        {

            //Execute
            using (Process proc = new Process())
            {
                proc.StartInfo = new ProcessStartInfo();

                if (item is Emulator)
                    proc.StartInfo.FileName = ThumbsHandler.Instance.createEmulatorArt((Emulator)item, "manual");
                else if (item is Game)
                    proc.StartInfo.FileName = ThumbsHandler.Instance.createGameArt((Game)item, "manual", false);

                proc.Start();
            }
        }

        public static void launchGame(Game game)
        {
            launchGame(game, false, null);
        }

        public static void launchGame(Game game, bool isConfig)
        {
            launchGame(game, isConfig, null);
        }

        public static void launchGame(Game game, EmulatorProfile profile)
        {
            launchGame(game, false, profile);
        }

        public static void launchGame(Game game, bool isConfig, EmulatorProfile profile)
        {
            //stop any media playing
            if (Options.getBoolOption("stopmediaplayback") && MediaPortal.Player.g_Player.Playing)
                MediaPortal.Player.g_Player.Stop();

            if (game.ParentEmulator.isPc())
            {
                //Run the standalone PC game
                try
                {
                    String path = game.Path;
                    String args = "";

                    //Takes away the switch from path if it exists
                    if (!path.EndsWith(".exe") && !path.EndsWith(".bat"))
                    {
                        args += path.Substring(path.LastIndexOf('.') + 5);
                        path = path.Remove(path.LastIndexOf('.') + 4);
                    }

                    if (!isConfig)
                    {
                        //Update statistics
                        game.Latestplay = DateTime.Now;
                        game.Playcount++;
                        DB.saveGame(game);
                    }

                    //Execute
                    Process proc = new Process();
                    proc.StartInfo = new ProcessStartInfo();
                    proc.StartInfo.FileName = path;
                    proc.StartInfo.Arguments = args;
                    proc.StartInfo.WorkingDirectory = Directory.GetParent(game.Path).FullName;


                    //Commented out the ability to toggle Mediaportal suspend for PC games
                    //because implementing it would be fairly ugly (the suspend_mp
                    //field would also need to be added to the games table since pc games
                    //have no emulator entry in the emulators table)

                    proc.EnableRaisingEvents = true;
                    if (!isConfig)
                    {
                        proc.Exited += new EventHandler(onGameExit);
                    }
                    else
                    {
                        proc.Exited += new EventHandler(defaultOnGameExit);
                    }
                    Logger.LogInfo("Launching PC Game {0}", path.Substring(path.LastIndexOf('\\') + 1));
                    if (!proc.Start())
                    {
                        Logger.LogError("Executor - Process did not start");
                        disposeProc();
                        return;
                    }
                    if (StatusChanged != null)
                        StatusChanged(true);

                    /* The following code was referenced from the Moving Pictures plugin */
                    // disable mediaportal input devices
                    if (!isConfig)
                    {
                        Logger.LogDebug("Suspending MediaPortal...");
                        InputDevices.Stop();

                        // hide mediaportal and suspend rendering to save resources for the pc game
                        GUIGraphicsContext.BlankScreen = true;
                        GUIGraphicsContext.form.Hide();
                        GUIGraphicsContext.CurrentState = GUIGraphicsContext.State.SUSPENDING;
                    }
                }
                catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.Message); }
            }
            else
            {
                //Run the emulator with the correct ROM
                try
                {
                    EmulatorProfile emuProfile;
                    if (profile != null && profile.EmulatorID == game.ParentEmulator.UID)
                        emuProfile = profile;
                    else
                        emuProfile = DB.GetSelectedProfile(game);

                    String path = emuProfile.EmulatorPath;
                    String args = emuProfile.Arguments;
                    String rompath = game.Path;

                    if (game.ParentEmulator.EnableGoodmerge)
                    {
                        rompath = Extractor.Instance.ExtractGame(game, emuProfile);
                    }

                    //To allow the use of arguments after the game file, use gameWildcard
                    if (!args.Contains(gameWildcard))
                    {
                        if (args.Length > 0)
                        {
                            args += " ";
                        }
                        args += gameWildcard;
                    }
                    if (emuProfile.UseQuotes == true)
                    {
                        args = args.Replace(gameWildcard, "\"" + rompath + "\"");
                    }
                    else
                    {
                        args = args.Replace(gameWildcard, rompath);
                    }

                    //To have the filename without the path and extension, use gameWithoutExtWildcard
                    if (args.Contains(gameWithoutExtWildcard))
                    {
                        String replacer = rompath.Remove(rompath.LastIndexOf('.'));
                        args = args.Replace(gameWithoutExtWildcard, replacer.Substring(replacer.LastIndexOf('\\') + 1));
                    }

                    if (!isConfig)
                    {
                        //Update statistics
                        game.Latestplay = DateTime.Now;
                        game.Playcount++;
                        DB.saveGamePlayInfo(game);
                    }
                    //Execute
                    proc = new Process();
                    proc.StartInfo = new ProcessStartInfo();
                    proc.StartInfo.FileName = path;
                    proc.StartInfo.Arguments = args;
                    if (emuProfile.WorkingDirectory.Length > 0)
                    {
                        proc.StartInfo.WorkingDirectory = emuProfile.WorkingDirectory;
                    }
                    else
                    {
                        proc.StartInfo.WorkingDirectory = Directory.GetParent(emuProfile.EmulatorPath).FullName;
                    }

                    proc.EnableRaisingEvents = true;
                    if (!isConfig && emuProfile.SuspendMP == true)
                    {
                        proc.Exited += new EventHandler(onGameExit);
                    }
                    else
                    {
                        proc.Exited += new EventHandler(defaultOnGameExit);
                    }

                    Logger.LogInfo("Launching {0} with Rom {1}", game.ParentEmulator.Title, rompath.Substring(rompath.LastIndexOf('\\') + 1));

                    if (!proc.Start())
                    {
                        Logger.LogError("Executor - Process did not start");
                        disposeProc();
                        return;
                    }

                    if (StatusChanged != null)
                        StatusChanged(true);

                    if (Options.getBoolOption("domap"))
                    {
                        //get key mapping and setup keyboard hook
                        string keyMap = Options.getStringOption("mappedkey");
                        if (!String.IsNullOrEmpty(keyMap))
                        {
                            Logger.LogInfo("Stop emulation on mapped key selected. Initialising keyboard hook...");
                            key = Keys.None;
                            try
                            {
                                key = (Keys)Enum.Parse(typeof(Keys), keyMap, true);
                            }
                            catch
                            {
                                key = Keys.None;
                                Logger.LogError("Keyboard hook - Error parsing key '{0}'", keyMap);
                            }
                            if (key != Keys.None)
                            {
                                Logger.LogDebug("Keyboard hook - Emulation will be stopped on '{0}' key", keyMap);
                                hook = new KeyboardHook(proc.Id, keyEvent);
                            }
                        }
                        else
                            Logger.LogError("Keyboard hook - No key has been mapped");
                    }
                    if (!isConfig && emuProfile.SuspendMP == true)
                    {
                        Logger.LogDebug("Suspending MediaPortal...");
                        /* The following code was referenced from the Moving Pictures plugin */
                        // disable mediaportal input devices
                        InputDevices.Stop();

                        // hide mediaportal and suspend rendering to save resources for the pc game
                        GUIGraphicsContext.BlankScreen = true;
                        GUIGraphicsContext.form.Hide();
                        GUIGraphicsContext.CurrentState = GUIGraphicsContext.State.SUSPENDING;
                    }
                }
                catch (Exception) { throw; }
            }
        }

        static KeyEventHandler keyEvent = new KeyEventHandler(onMappedKey);
        static void onMappedKey(object sender, KeyEventArgs e)
        {
            if (e.KeyData == key)
            {
                Logger.LogDebug("Keyboard hook - Mapped key pressed, stopping emulation");
                e.Handled = true;
                e.SuppressKeyPress = true;
                proc.CloseMainWindow();
                if (!proc.HasExited) //needed for epsxe
                    proc.Kill();
            }
        }

        static void disposeHook()
        {
            if (hook != null)
            {
                Logger.LogDebug("Keyboard hook - Disposing...");
                hook.Dispose();
                hook = null;
            }
        }

        static void disposeProc()
        {
            if (proc != null)
            {
                proc.Dispose();
                proc = null;
            }
        }

        static void defaultOnGameExit(object obj, EventArgs e)
        {
            disposeHook();
            disposeProc();

            if (StatusChanged != null)
                StatusChanged(false);
        }

        static void onGameExit(object obj, EventArgs e)
        {
            disposeHook();
            disposeProc();
            /* The following code was referenced from the Moving Pictures plugin */
            // Enable Mediaportal input devices
            InputDevices.Init();

            // Resume Mediaportal rendering
            GUIGraphicsContext.BlankScreen = false;
            GUIGraphicsContext.form.Show();
            GUIGraphicsContext.ResetLastActivity();
            GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_GETFOCUS, 0, 0, 0, 0, 0, null);
            GUIWindowManager.SendThreadMessage(msg);
            GUIGraphicsContext.CurrentState = GUIGraphicsContext.State.RUNNING;

            if (StatusChanged != null)
                StatusChanged(false);
        }
    }
}
